// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-01-01 // Contains ... using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; using System.Text; using System.Xml.Linq; using System.Xml.Serialization; using LargoCommon.Abstract; using LargoCommon.Interfaces; using LargoCommon.Midi; namespace LargoCommon.Music { /// /// Musical Strike Collection. /// [Serializable] [XmlRoot] public sealed class MusicalStrikeCollection : Collection { #region Constructors /// /// Initializes a new instance of the MusicalStrikeCollection class. /// public MusicalStrikeCollection() { } /// /// Initializes a new instance of the MusicalStrikeCollection class. /// /// Given list. public MusicalStrikeCollection(IList givenList) : base(givenList) { } /// /// Initializes a new instance of the class. /// /// The Xml tones. /// The rhythmic order. public MusicalStrikeCollection(XElement xmlTones, byte rhythmicOrder) { Contract.Requires(xmlTones != null); byte lastInstrument = (byte)MidiMelodicInstrument.None; foreach (var xtone in xmlTones.Elements()) { IMusicalTone mt = null; switch (xtone.Name.ToString()) { case "Tone": { mt = new MusicalTone(xtone, rhythmicOrder); break; } case "Pause": { mt = new MusicalPause(xtone, rhythmicOrder); break; } case "Tick": { mt = new MusicalStrike(xtone, rhythmicOrder); break; } } if (mt == null) { continue; } //// 2019/02- instrument is also property of pauses ... if (mt.InstrumentNumber == (byte)MidiMelodicInstrument.None) { mt.InstrumentNumber = lastInstrument; } lastInstrument = mt.InstrumentNumber; //// mt.BarNumber = LibSupport.ReadIntegerAttribute(xtone.Attribute("BarNumber")); this.Add(mt); } } #endregion #region Properties - Xml /// Gets String representation of the object. /// Returns value. public XElement GetXElement { get { var xtones = new XElement("Tones", null); foreach (var xtone in this.Select(mt => mt.GetXElement).Where(xtone => xtone != null)) { xtones.Add(xtone); } return xtones; } } #endregion #region Properties /// /// Gets or sets Name ( there was an Identifier in the Harmonic Stream Ground class ...). /// /// Property description. public string Name { get; set; } /// /// Gets Count Of MelTones. /// /// Property description. public int CountOfMelTones { get { if (this.Count == 0) { return 0; } var num = (from mt in this where mt.ToneType == MusicalToneType.Melodic select 1).Count(); return num; } } /// /// Gets the count of sounding tones. /// /// Property description. public int CountOfSoundingTones { get { if (!this.HasAnySoundingTone) { return 0; } var num = (from mt in this where mt.ToneType == MusicalToneType.Melodic && !mt.IsPause select 1).Count(); return num; } } /// Gets Mean Loudness. /// Property description. public MusicalLoudness MeanLoudness { get { const int limit = 100; float sumLoudness = 0; var numNotes = 0; var tones = this.Where(mt => mt != null && mt.ToneType != MusicalToneType.Empty); foreach (var mt in tones) { if (mt is MusicalStrike mtone && mtone.Loudness > 0) { sumLoudness += (short)mtone.Loudness; numNotes++; if (numNotes >= limit) { break; } } } var meanLoudness = numNotes != 0 ? sumLoudness / numNotes : 0; return (MusicalLoudness)(byte)Math.Round(meanLoudness); } } /// /// Gets the mean midi key number. /// /// /// The mean midi key number. /// public byte MeanMidiKeyNumber { get { const int limit = 50; //// .Take(50) float sumMidiKeys = 0; var numNotes = 0; // ReSharper disable once LoopCanBePartlyConvertedToQuery foreach (var musicalStrike in this.Where(musicalStrike => musicalStrike != null && !musicalStrike.IsPause)) { MusicalTone musicalTone = musicalStrike as MusicalTone; if (musicalTone == null || musicalTone.IsEmpty) { continue; } sumMidiKeys += musicalTone.Pitch.MidiKeyNumber; numNotes++; if (numNotes >= limit) { break; } } var meanMidiKey = numNotes != 0 ? sumMidiKeys / numNotes : 0; return (byte)Math.Round(meanMidiKey); } } /// /// Gets the mean octave. /// /// Property description. public MusicalOctave MeanOctave { get { const int limit = 50; //// .Take(50) float sumOctave = 0; var numNotes = 0; // ReSharper disable once LoopCanBePartlyConvertedToQuery foreach (var musicalStrike in this.Where(musicalStrike => musicalStrike != null && !musicalStrike.IsPause)) { MusicalTone musicalTone = musicalStrike as MusicalTone; if (musicalTone == null || musicalTone.IsEmpty) { continue; } sumOctave += musicalTone.Pitch.Octave; numNotes++; if (numNotes >= limit) { break; } } var meanOctave = numNotes != 0 ? sumOctave / numNotes : 0; return (MusicalOctave)(byte)Math.Round(meanOctave); } } /// /// Gets the first instrument. /// /// Property description. public MidiMelodicInstrument FirstMelodicInstrument { get { var instrument = MidiMelodicInstrument.None; // ReSharper disable once LoopCanBePartlyConvertedToQuery foreach (var musicalStrike in this.Where(musicalStrike => musicalStrike != null && !musicalStrike.IsPause)) { MusicalTone musicalTone = musicalStrike as MusicalTone; if (musicalTone == null || musicalTone.IsEmpty) { continue; } instrument = (MidiMelodicInstrument)musicalTone.InstrumentNumber; break; } return instrument; } } /// /// Gets the first rhythmic instrument. /// /// /// The first rhythmic instrument. /// public MidiRhythmicInstrument FirstRhythmicInstrument { get { var instrument = MidiRhythmicInstrument.None; // ReSharper disable once LoopCanBePartlyConvertedToQuery foreach (var musicalStrike in this.Where(musicalStrike => musicalStrike != null && !musicalStrike.IsPause)) { instrument = (MidiRhythmicInstrument)musicalStrike.InstrumentNumber; break; } return instrument; } } /// /// Gets a value indicating whether [has any sounding tone]. /// /// Property description. /// /// True if [has any sounding tone]; otherwise, false. /// public bool HasAnySoundingTone { get { return this.Count != 0 && this.Any(mt => !mt.IsPause); } } /// /// Gets a value indicating whether this instance has true tones. /// /// /// true if this instance has true tones; otherwise, false. /// public bool HasTrueTones { get { foreach (var musicalStrike in this.Where(musicalStrike => musicalStrike != null && !musicalStrike.IsPause)) { if (!(musicalStrike is MusicalTone musicalTone) || musicalTone.IsEmpty) { return false; } } return true; } } /// /// Gets the melodic pattern identifier. /// /// /// The melodic pattern identifier. /// public string MelodicPatternIdentifier { get { var sb = new StringBuilder("Idents"); foreach (var ident in this.Select(mt => mt.MelodicIdentifier).Where(xtone => xtone != null)) { sb.Append(ident); } return sb.ToString(); } } /// /// Gets the rhythmic pattern identifier. /// /// /// The rhythmic pattern identifier. /// public string RhythmicPatternIdentifier { get { var sb = new StringBuilder("Idents"); foreach (var ident in this.Select(mt => mt.RhythmicIdentifier).Where(xtone => xtone != null)) { sb.Append(ident); } return sb.ToString(); } } #endregion #region Public static /// /// Gets the tones from Midi tones. /// /// The given header. /// The midi tones. /// if set to true [melodic track]. /// Returns value. public static MusicalStrikeCollection GetTones(MusicalHeader givenHeader, IMidiTones midiTones, bool melodicTrack) { //// this.Strip.Context.Header Contract.Requires(midiTones != null); //// Contract.Requires(track.Sequence != null); Contract.Requires(givenHeader.Metric.MetricGround > 0); if (midiTones == null) { //// || this.MusicalBlock.Metric.MetricGround == 0 return null; } var rhythmicOrder = givenHeader.System.RhythmicOrder; //// !!!!! int division = track.Sequence.Division; //// int division = this.MusicalBlock.Division; //// track.BarDivision = MusicalProperties.BarDivision(division, this.MusicalBlock.MetricBeat, this.MusicalBlock.MetricGround); //// rhythmicOrder must be integer divisor of barDivision var quotient = midiTones.BarDivision / rhythmicOrder; //// !!!!! if (track.MidiTones.Count == 0 && track.Events.Count < 0) { track.ReadMidiTones(track); } var mtones = new List(); if ((midiTones.List.Count == 0) || (quotient == 0)) { return null; } //// int numberOfBarsInTrack = midiTones.NumberOfBars; //// this.MusicalBlock.NumberOfBars = Math.Max(this.MusicalBlock.NumberOfBars, numberOfBarsInTrack); //// int test = 0; foreach (var midiTone in midiTones.List) { for (var barNumber = midiTone.BarNumberFrom; barNumber <= midiTone.BarNumberTo; barNumber++) { var realBarNumber = midiTones.FirstBarNumber + barNumber - 1; //// if (midiTone.Note == "A#1" && realBarNumber >= 3) { //// && this.MusicalBlock.NumberOfBars == 32 //// test = 1; } //// test = 2 * test; var musTone = MusicalStrike.GetNewMusicalTone(givenHeader, quotient, midiTone, barNumber, realBarNumber, melodicTrack); if (musTone == null) { continue; } mtones.Add(musTone); } } var selectedTones = (from tone in mtones orderby tone.BarNumber, tone.BitFrom select tone).ToList(); var tones = new MusicalStrikeCollection(selectedTones); //// if (tones == null) { return null; } var completedTones = tones.CollectionWithAddedMissingPauses(); //// if (completedTones == null) { return null; } var standardizedTones = completedTones.StandardizeTones(givenHeader); return standardizedTones; } #endregion #region Public methods /// /// Add on musical tone to the end of part. /// /// Musical tone. /// If set to true [set ordinal index]. public void AddTone(IMusicalTone givenTone, bool setOrdinalIndex) { Contract.Requires(givenTone != null); //// if (givenTone == null) { return false; } if (setOrdinalIndex) { givenTone.OrdinalIndex = this.Count; } this.Add(givenTone); } /// /// Adds the collection. /// /// The given tones. /// If set to true [set ordinal index]. public void AddCollection(MusicalStrikeCollection givenTones, bool setOrdinalIndex) { Contract.Requires(givenTones != null); givenTones.ForAll(musicalStrike => this.AddTone(musicalStrike, setOrdinalIndex)); } /// /// Adds the collection. /// /// The given tones. /// The given bar number. /// if set to true [set ordinal index]. public void AddCollection(MusicalStrikeCollection givenTones, int givenBarNumber, bool setOrdinalIndex) { Contract.Requires(givenTones != null); foreach (var tone in givenTones) { tone.BarNumber = givenBarNumber; } this.AddCollection(givenTones, setOrdinalIndex); } /// /// Add Missing Pauses. /// /// if set to true [reset bar]. /// /// Returns value. /// public MusicalStrikeCollection Clone(bool resetBar) { var newTones = new MusicalStrikeCollection(); foreach (var newTone in this.Select(tone => (IMusicalTone)tone.CloneTone())) { if (resetBar) { newTone.BarNumber = 1; } newTones.Add(newTone); } return newTones; } /// Return number of bits with non-harmonic tones. /// Returns value. /// Harmonic structure. /// Rhythmic range. public byte NumberOfMelodicBits( BinaryStructure harmonicStruct, BitRange rhythmicRange) { Contract.Requires(harmonicStruct != null); Contract.Requires(rhythmicRange != null); //// if (harmonicStruct == null) { return 0; } if (rhythmicRange == null) { return 0; } var tones = from IMusicalTone mt in this where mt != null && !mt.IsPause select mt as MusicalTone; var interRanges = from MusicalTone mt in tones where mt != null && !mt.IsEmpty && !harmonicStruct.IsOn(mt.Pitch.Element) select mt.BitRange into toneRange //// (barNumber) select rhythmicRange.IntersectionWith(toneRange) into interRange where interRange != null select interRange; var totalLength = interRanges.Aggregate(0, (current, interRange) => (byte)(current + interRange.Length)); return totalLength; } /// /// Add Missing Pauses. /// /// Returns value. [JetBrains.Annotations.PureAttribute] public MusicalStrikeCollection CollectionWithAddedMissingPauses() { var firstTone = this.FirstOrDefault(); if (firstTone == null) { return null; } var lastBarNumber = firstTone.BarNumber - 1; var rhythmicOrder = firstTone.RhythmicOrder; //// Pause for previous bars var newTones = CollectionWithPauses(lastBarNumber, rhythmicOrder); //// Pause before the first tone if (firstTone.BitFrom > 0) { IMusicalTone pause = MusicalPause.CreatePause(firstTone.RhythmicOrder, 0, firstTone.BitFrom, firstTone.BarNumber); pause.InstrumentNumber = firstTone.InstrumentNumber; //// 2019/02 newTones.Add(pause); } newTones.Add(firstTone); var lastTone = firstTone; this.AddMissingInternalPauses(lastTone, newTones); return newTones; } /// /// Standardizes the tones. /// /// The given header. /// /// Returns value. /// [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1126:PrefixCallsCorrectly", Justification = "Reviewed.")] [JetBrains.Annotations.PureAttribute] public MusicalStrikeCollection StandardizeTones(MusicalHeader givenHeader) { var newTones = new MusicalStrikeCollection(); var beat = givenHeader.Metric.MetricBeat; if (beat == 0) { return null; } var ground = givenHeader.Metric.MetricGround; int rorder = givenHeader.System.RhythmicOrder; var wholeNoteTicks = rorder * ground / beat; MusicalTone lastMelodicTone = null; foreach (var tone in this) { //// Include pause to the previous tone. if (tone is MusicalTone musicalTone && !musicalTone.IsEmpty) { newTones.Add(musicalTone); lastMelodicTone = musicalTone; continue; } NoteLength noteLength; if (lastMelodicTone != null) { var musicalPause = tone as MusicalPause ?? new MusicalPause(tone.BitRange, tone.BarNumber); noteLength = NoteLength.GetNoteLength(lastMelodicTone.Duration, musicalPause.Duration, wholeNoteTicks); lastMelodicTone.NoteLength = noteLength; if (noteLength != null && noteLength.IncludePause && lastMelodicTone.Pause == null) { //// added lastMelodicTone.Pause == null !?! lastMelodicTone.Pause = musicalPause; } else { noteLength = NoteLength.GetNoteLength(musicalPause.Duration, 0, wholeNoteTicks); musicalPause.NoteLength = noteLength; newTones.Add(musicalPause); } } else { noteLength = NoteLength.GetNoteLength(tone.Duration, 0, wholeNoteTicks); tone.NoteLength = noteLength; newTones.Add(tone); } } return newTones; } /// /// Determine RhythmicStructure. /// /// Rhythmical order. /// Returns value. public RhythmicStructure DetermineRhythmicStructure(byte rhythmicOrder) { var tonesGroups = from mt in this group mt by mt.BitFrom; var tonesDistinct = new Collection(); foreach (var toneGroup in tonesGroups) { tonesDistinct.Add(toneGroup.First()); } var rstruct = new RhythmicStructure(rhythmicOrder, tonesDistinct); rstruct.DetermineBehavior(); return rstruct; } /// /// Write tones into MIDI-list. /// /// Midi Event List. /// Melodic instrument. /// Bar division. public void WriteTo(MidiEventCollection midiEvents, byte instrument, int barDivision) { //// , MidiChannel channel ////byte rhyOrder, if (midiEvents == null) { return; } foreach (var mt in this) { MusicalStrike mtone = mt as MusicalStrike; if (mtone == null) { continue; } mtone.InstrumentNumber = instrument; //// mt.Channel = channel; mt.WriteTo(midiEvents, barDivision); } } /// /// Export To Midi Events. /// /// Midi Events. /// Midi instrument. /// Midi channel. /// Bar division. public void ExportToMidiEvents(MidiEventCollection events, byte instrument, MidiChannel channel, int barDivision) { Contract.Requires(events != null); if (events == null) { return; } if (channel != MidiChannel.DrumChannel) { events.PutInstrument(0, instrument); } foreach (var mt in this.Where(mt => mt != null && mt.RhythmicOrder > 0)) { MusicalStrike mtone = mt as MusicalStrike; if (mtone == null) { continue; } mtone.InstrumentNumber = instrument; //// mt.Channel = channel; mt.WriteTo(events, barDivision); } } /// /// Sets the instrument. /// /// The instrument. /// The octave. public void SetInstrument(byte instrument, MusicalOctave octave) { var firstTone = this.FirstOrDefault(); foreach (var mt in this) { //// 2015/01 // ReSharper disable once PossibleUnintendedReferenceComparison MusicalStrike mtone = mt as MusicalStrike; if (mtone == null) { continue; } // ReSharper disable once PossibleUnintendedReferenceComparison if (mtone.IsFromPreviousBar && mtone == firstTone) { continue; } mtone.InstrumentNumber = instrument; //// mt.Channel = (MidiChannel)channel; if (mtone is MusicalTone melt && !melt.IsEmpty) { melt.Pitch.Octave = (short)octave; } } } #endregion #region String representation /// /// String with tones. /// /// Returns value. public string TonesToString() { var s = new StringBuilder(); var actualBarNumber = 0; foreach (var mt in this.Where(mt => mt != null)) { if (mt.BarNumber != actualBarNumber) { s.Append("|| "); ////‡↕#∫$ actualBarNumber = mt.BarNumber; } s.Append(mt); } return s.ToString(); } /// /// Rhythmic tones to string. /// /// Returns value. public string RhythmicTonesToString() { var s = new StringBuilder(); var actualBarNumber = 0; foreach (var mt in this.Where(mt => mt != null)) { if (mt.BarNumber != actualBarNumber) { s.Append("|| "); ////‡↕#∫$ actualBarNumber = mt.BarNumber; } if (mt is MusicalStrike mx) { s.Append(mx.RhythmicToString()); } } return s.ToString(); } /// /// Returns the tooltip. /// /// Returns value. public string TooltipString() { var s = new StringBuilder(); this.ForAll(musicalStrike => s.AppendLine(musicalStrike.ToString())); return s.ToString(); } /// String representation of the object. /// Returns value. public override string ToString() { var s = new StringBuilder(); this.ForAll(musicalStrike => s.Append(musicalStrike)); return s.ToString(); } #endregion #region Private methods /// /// Collections the with pauses. /// /// The last bar number. /// The rhythmic order. /// Returns value. private static MusicalStrikeCollection CollectionWithPauses(int lastBarNumber, byte rhythmicOrder) { var newTones = new MusicalStrikeCollection(); for (var barNumber = 1; barNumber <= lastBarNumber; barNumber++) { IMusicalTone pause = MusicalPause.CreatePause(rhythmicOrder, 0, rhythmicOrder, barNumber); newTones.Add(pause); } return newTones; } /// /// Adds the missing internal pauses. /// /// The last tone. /// The new tones. private void AddMissingInternalPauses(IMusicalTone lastTone, MusicalStrikeCollection newTones) { foreach (var tone in this) { //// Pause between the last tone and tone int bitFrom; int duration; if (lastTone.BarNumber == tone.BarNumber) { bitFrom = lastTone.BitFrom + lastTone.Duration; duration = tone.BitFrom - bitFrom; //// tone.BitPosition - lastTone.BitPosition; //// 2016/08 - in the property Pause is saved short pause after tone (staccato,...) var mtone = lastTone as MusicalTone; if (duration > 0 && (mtone?.Pause == null)) { IMusicalTone pause = MusicalPause.CreatePause(lastTone.RhythmicOrder, (byte)bitFrom, (byte)duration, lastTone.BarNumber); pause.InstrumentNumber = lastTone.InstrumentNumber; //// 2019/02 newTones.Add(pause); } } else { if (lastTone.BarNumber < tone.BarNumber) { //// Pause after the last tone of previous bar in the previous bar bitFrom = lastTone.BitFrom + lastTone.Duration; duration = lastTone.RhythmicOrder - bitFrom; if (duration > 0) { IMusicalTone pause = MusicalPause.CreatePause(lastTone.RhythmicOrder, (byte)bitFrom, (byte)duration, lastTone.BarNumber); pause.InstrumentNumber = lastTone.InstrumentNumber; //// 2019/02 newTones.Add(pause); } //// 2016/08 if (tone.BitFrom > 0) { bitFrom = 0; duration = tone.BitFrom; IMusicalTone pause = MusicalPause.CreatePause(lastTone.RhythmicOrder, (byte)bitFrom, (byte)duration, lastTone.BarNumber); pause.InstrumentNumber = lastTone.InstrumentNumber; //// 2019/02 newTones.Add(pause); } } } //// ReSharper disable once PossibleUnintendedReferenceComparison //// tone != lastTone) if (!object.ReferenceEquals(tone, lastTone)) { newTones.Add(tone); } lastTone = tone; } } #endregion } }